home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Aminet 1 (Walnut Creek)
/
Aminet - June 1993 [Walnut Creek].iso
/
usenet
/
sources
/
volume90
/
devices
/
msh_1_5
/
part03
< prev
next >
Wrap
Internet Message Format
|
1990-02-21
|
42KB
Path: xanth!cs.odu.edu!Amiga-Request
From: Amiga-Request@cs.odu.edu (Amiga Sources/Binaries Moderator)
Newsgroups: comp.sources.amiga
Subject: v90i081: MSH 1.5 - Messydos File System Handler , Part03/06
Message-ID: <11500@xanth.cs.odu.edu>
Date: 21 Feb 90 01:59:44 GMT
Sender: tadguy@cs.odu.edu
Reply-To: Olaf 'Rhialto' Seibert <U211344%HNYKUN11.BITNET@CUNYVM.CUNY.EDU>
Lines: 1734
Approved: tadguy@cs.odu.edu (Tad Guy)
X-Mail-Submissions-To: Amiga@cs.odu.edu
X-Post-Discussions-To: comp.sys.amiga
Submitted-by: Olaf 'Rhialto' Seibert <U211344%HNYKUN11.BITNET@CUNYVM.CUNY.EDU>
Posting-number: Volume 90, Issue 081
Archive-name: devices/msh-1.5/part03
#!/bin/sh
# This is a shell archive. Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file". To overwrite existing
# files, type "sh file -c". You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g.. If this archive is complete, you
# will see the following message at the end:
# "End of archive 3 (of 6)."
# Contents: src/device.c src/hansec.c
# Wrapped by tadguy@xanth on Tue Feb 20 20:57:10 1990
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'src/device.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'src/device.c'\"
else
echo shar: Extracting \"'src/device.c'\" \(18725 characters\)
sed "s/^X//" >'src/device.c' <<'END_OF_FILE'
X/*-
X * $Id: device.c,v 1.5 90/01/27 20:34:43 Rhialto Exp $
X * $Log: device.c,v $
X * Revision 1.5 90/01/27 20:34:43 Rhialto
X * Commented out #undef DEBUG
X *
X * Revision 1.4 90/01/23 00:24:50 Rhialto
X * io_Error=0 for immediate commands
X *
X * Revision 1.3 89/12/17 21:29:37 Rhialto
X * Revision 1.1 89/12/17 20:03:55 Rhialto
X *
X * DEVICE.C
X *
X * The messydisk.device code that makes it a real Exec .device.
X * Mostly based on the 1.1 RKM example and Matt Dillon's library code.
X *
X * This code is (C) Copyright 1989 by Olaf Seibert. All rights reserved. May
X * not be used or copied without a licence.
X-*/
X
X#include "dev.h"
X#include "device.h"
X
X/*#undef DEBUG /**/
X#ifdef DEBUG
X# define debug(x) dbprintf x
X#else
X# define debug(x)
X#endif
X
X/*
X * The first executable location. This should return an error in case
X * someone tried to run you as a program (instead of loading you as a
X * device)
X */
X/* INDENT OFF */
X#asm
X moveq.l #20,d0
X rts
X#endasm
X/* INDENT ON */
X
X/*
X * A romtag structure. Both "exec" and "ramlib" look for this structure to
X * discover magic constants about you (such as where to start running you
X * from...).
X */
X/* INDENT OFF */
X#asm
X public __H0_end
X_EndCode equ __H0_end
X public _RomTag
X_RomTag:
X dc.w $4AFC ; RTC_MATCHWORD
X dc.l _RomTag ; rt_MatchTag
X dc.l __H0_end ; rt_EndSkip
X dc.b 0 ; rt_Flags (no RTF_AUTOINIT)
X dc.b VERSION ; rt_Version
X dc.b 3 ; rt_Type NT_DEVICE
X dc.b RTPRI ; rt_Pri
X dc.l _DevName ; rt_Name
X dc.l _idString ; rt_IdString
X dc.l _Init ; rt_Init
X#endasm
X/* INDENT ON */
X
Xchar DevName[] = "messydisk.device";
Xchar idString[] = "messydisk.device $Revision: 1.5 $ $Date: 90/01/27 20:34:43 $\r\n";
X
X/*
X * -30-6*X Library vectors:
X */
X
Xvoid (*LibVectors[]) () =
X{
X _DevOpen, _DevClose, _DevExpunge, _LibNull,
X
X _DevBeginIO,
X _DevAbortIO,
X (void (*) ()) -1
X};
X
X/*
X * Device commands:
X */
X
Xvoid (*funcTable[]) () = {
X CMD_Invalid, CMD_Reset, CMD_Read, CMD_Write, CMD_Update, CMD_Clear,
X CMD_Stop, CMD_Start, CMD_Flush, TD_Motor, TD_Seek, TD_Format,
X TD_Remove, TD_Changenum, TD_Changestate, TD_Protstatus, TD_Rawread,
X TD_Rawwrite, TD_Getdrivetype, TD_Getnumtracks, TD_Addchangeint,
X TD_Remchangeint,
X};
X
X/*
X * Here begin the system interface commands. When the user calls
X * OpenDevice/CloseDevice/RemDevice, this eventually gets trahslated into
X * a call to the following routines (Open/Close/Expunge). Exec has
X * already put our device pointer in A6 for us. Exec has turned off task
X * switching while in these routines (via Forbid/Permit), so we should not
X * take too long in them.
X */
X/* INDENT OFF */
X#asm
X public _Init
X_Init: ;a0=segment list
X movem.l D2-D3/A0/A6,-(sp)
X jsr _CInit
X movem.l (sp)+,D2-D3/A0/A6
X rts
X
X public __DevOpen
X__DevOpen: ;d0=unitnum,d1=flags,a1=ioreq,a6=device
X movem.l D0-D3/A1/A6,-(sp)
X jsr _DevOpen
X movem.l (sp)+,D0-D3/A1/A6
X rts
X
X public __DevClose
X__DevClose: ;a1=ioreq,a6=device
X movem.l D2-D3/A1/A6,-(sp)
X jsr _DevClose
X movem.l (sp)+,D2-D3/A1/A6
X rts
X
X public __DevExpunge
X__DevExpunge: ;a6=device
X movem.l D2-D3/A6,-(sp)
X jsr _DevExpunge
X movem.l (sp)+,D2-D3/A6
X rts
X
X public __LibNull
X__LibNull:
X clr.l d0
X rts
X
X public __DevBeginIO
X__DevBeginIO: ;a1=ioreq,a6=device
X movem.l D2-D3/A1/A6,-(sp)
X jsr _DevBeginIO
X movem.l (sp)+,D2-D3/A1/A6
X rts
X
X public __DevAbortIO
X__DevAbortIO: ;a1=ioreq,a6=device
X movem.l D2-D3/A1/A6,-(sp)
X jsr _DevAbortIO
X movem.l (sp)+,D2-D3/A1/A6
X rts
X#endasm
X
X#ifdef HANDLE_IO_QUICK
X#asm
X;;;;
X;
X; C interface to the atomic set bit and test old value instruction.
X;
X; Called as BSET_ACTIVE(byte *address).
X;
X; Old value of the bit returned all over d0.w
X
X_BSET_ACTIVE:
X move.l 4(sp),a0
X bset #0,(a0) ; UNITB_ACTIVE
X sne d0
X rts
X
X#endasm
X#endif
X/* INDENT ON */
X
Xlong SysBase; /* Argh! A global variable! */
X
X/*
X * The Initialization routine is given only a seglist pointer. Since we
X * are NOT AUTOINIT we must construct and add the device ourselves and
X * return either NULL or the device pointer. Exec has Forbid() for us
X * during the call.
X *
X * If you have an extended device structure you must specify the size of the
X * extended structure in MakeLibrary().
X */
X
XDEV *
XCInit(D2, D3, segment)
Xulong D2,
X D3;
Xlong segment;
X{
X DEV *dev;
X
X SysBase = *(long *) 4;
X#ifdef DEBUG
X dbinit();
X#endif
X dev = MakeLibrary(LibVectors, NULL, NULL, (long) sizeof (DEV), NULL);
X if (DevInit(dev)) {
X dev->dev_Node.ln_Type = NT_DEVICE;
X dev->dev_Node.ln_Name = DevName;
X dev->dev_Flags = LIBF_CHANGED | LIBF_SUMUSED;
X dev->dev_Version = VERSION;
X dev->dev_Revision = REVISION;
X dev->dev_IdString = (APTR) idString;
X dev->md_Seglist = segment;
X AddDevice(dev);
X return (dev);
X }
X FreeMem((char *) dev - dev->dev_NegSize, dev->dev_NegSize + dev->dev_PosSize);
X return NULL;
X}
X
X/*
X * Open is given the device pointer, unitno and flags. Either return the
X * device pointer or NULL. Remove the DELAYED-EXPUNGE flag. Exec has
X * Forbid() for us during the call.
X */
X
Xvoid
XDevOpen(unitno, flags, D2, D3, ioreq, dev)
Xulong unitno;
Xulong flags;
Xulong D2,
X D3;
Xstruct IOStdReq *ioreq;
XDEV *dev;
X{
X UNIT *unit;
X
X debug(("OpenDevice unit %ld, flags %lx\n", unitno, flags));
X if (unitno >= MD_NUMUNITS)
X goto error;
X
X if ((unit = dev->md_Unit[unitno]) == NULL) {
X if ((unit = UnitInit(dev, unitno)) == NULL)
X goto error;
X dev->md_Unit[unitno] = unit;
X }
X ioreq->io_Unit = (struct Unit *) unit;
X
X ++unit->mu_OpenCnt;
X ++dev->dev_OpenCnt;
X dev->dev_Flags &= ~LIBF_DELEXP;
X
X return;
X
Xerror:
X ioreq->io_Error = IOERR_OPENFAIL;
X}
X
X/*
X * Close is given the device pointer and the io request. Be sure not to
X * decrement the open count if already zero. If the open count is or
X * becomes zero AND there is a LIBF_DELEXP, we expunge the device and
X * return the seglist. Otherwise we return NULL.
X *
X * Note that this routine never sets LIBF_DELEXP on its own.
X *
X * Exec has Forbid() for us during the call.
X */
X
Xlong
XDevClose(D2, D3, ioreq, dev)
Xulong D2,
X D3;
Xstruct IOStdReq *ioreq;
XDEV *dev;
X{
X UNIT *unit;
X
X unit = (UNIT *) ioreq->io_Unit;
X debug(("CloseDevice io %08lx unit %08lx\n", ioreq, unit));
X
X /*
X * See if the unit is still in use. If not, close it down. This may
X * need to do an update, which requires the ioreq.
X */
X
X if (unit->mu_OpenCnt && --unit->mu_OpenCnt == 0) {
X dev->md_Unit[unit->mu_UnitNr] = NULL;
X UnitCloseDown(ioreq, dev, unit);
X }
X /*
X * Make sure the ioreq is not used again.
X */
X ioreq->io_Unit = (void *) -1;
X ioreq->io_Device = (void *) -1;
X
X if (dev->dev_OpenCnt && --dev->dev_OpenCnt)
X return (NULL);
X if (dev->dev_Flags & LIBF_DELEXP)
X return (DevExpunge(D2, D3, dev));
X return (NULL);
X}
X
X/*
X * We expunge the device and return the Seglist ONLY if the open count is
X * zero. If the open count is not zero we set the DELAYED-EXPUNGE
X * flag and return NULL.
X *
X * Exec has Forbid() for us during the call. NOTE ALSO that Expunge might be
X * called from the memory allocator and thus we CANNOT DO A Wait() or
X * otherwise take a long time to complete (straight from RKM).
X *
X * Apparently RemLibrary(lib) calls our expunge routine and would therefore
X * freeze if we called it ourselves. As far as I can tell from RKM,
X * DevExpunge(lib) must remove the device itself as shown below.
X */
X
Xlong
XDevExpunge(D2, D3, dev)
Xulong D2,
X D3;
XDEV *dev;
X{
X long Seglist;
X
X if (dev->dev_OpenCnt) {
X dev->dev_Flags |= LIBF_DELEXP;
X return (NULL);
X }
X Remove(dev);
X DevCloseDown(dev); /* Should be quick! */
X#ifdef DEBUG
X dbuninit();
X#endif
X Seglist = dev->md_Seglist;
X FreeMem((char *) dev - dev->dev_NegSize,
X (long) dev->dev_NegSize + dev->dev_PosSize);
X return (Seglist);
X}
X
X/*
X * BeginIO entry point. We don't handle any QUICK requests, we just send
X * the request to the proper unit to handle.
X */
X
Xvoid
XDevBeginIO(D2, D3, ioreq, dev)
Xulong D2,
X D3;
Xregister struct IOStdReq *ioreq;
XDEV *dev;
X{
X UNIT *unit;
X
X /*
X * Bookkeeping.
X */
X unit = (UNIT *) ioreq->io_Unit;
X debug(("BeginIO: io %08lx dev %08lx u %08lx\n", ioreq, dev, unit));
X
X /*
X * See if the io command is within range.
X */
X if (STRIP(ioreq->io_Command) > TD_LASTCOMM)
X goto NoCmd;
X
X#ifdef HANDLE_IO_QUICK
X Forbid(); /* Disable(); is a bit too strong for us. */
X#endif
X
X /*
X * Process all immediate commands no matter what. Don't even require
X * an exclusive lock on the unit.
X */
X if (IMMEDIATE & (1L << STRIP(ioreq->io_Command)))
X goto Immediate;
X
X /*
X * We don't handle any QUICK I/O since that only gives trouble with
X * message ports and so. Other devices normally would include the code
X * below.
X */
X#ifdef HANDLE_IO_QUICK
X /*
X * See if the user does not request QUICK IO. If not, it is likely to
X * be async and therefore we don't do it sync.
X */
X if (!(ioreq->io_Flags & IOF_QUICK))
X goto NoQuickRequested;
X
X /*
X * See if the unit is STOPPED. If so, queue the msg.
X */
X if (unit->mu_Flags & UNITF_STOPPED)
X goto QueueMsg;
X
X /*
X * This is not an immediate command. See if the device is busy. If
X * not, process the action in this (the caller's) context.
X */
X if (!BSET_ACTIVE(&unit->mu_Flags))
X goto Immediate;
X#endif
X
X /*
X * We need to queue the device. Clear the QUICK flag.
X */
XQueueMsg:
X ioreq->io_Flags &= ~IOF_QUICK;
XNoQuickRequested:
X#ifdef HANDLE_IO_QUICK
X Permit(); /* Enable(); is a bit too strong for us. */
X#endif
X PutMsg(&unit->mu_Port, ioreq);
X
X return;
X
XImmediate:
X#ifdef HANDLE_IO_QUICK
X Permit(); /* Enable(); is a bit too strong for us. */
X#endif
X debug(("BeginIO: Immediate\n"));
X ioreq->io_Error = TDERR_NoError;
X PerformIO(ioreq, unit);
X return;
X
XNoCmd:
X ioreq->io_Error = IOERR_NOCMD;
X TermIO(ioreq);
X return;
X
X}
X
X/*
X * Terminate an io request. Called (normally) for every BeginIO. 'Funny'
X * commands that don't call TermIO, or call it multiple times, may not be
X * properly handled unless you are careful. TD_ADDCHANGEINT and
X * TD_REMCHANGEINT are obvious examples.
X */
X
Xvoid
XTermIO(ioreq)
Xregister struct IOStdReq *ioreq;
X{
X register UNIT *unit;
X
X unit = (UNIT *) ioreq->io_Unit;
X debug(("TermIO: io %08lx u %08lx %ld %d\n", ioreq, unit,
X ioreq->io_Actual, ioreq->io_Error));
X
X#ifdef HANDLE_IO_QUICK
X /*
X * Since immediate commands don't even require an exclusive lock on
X * the unit, don't unlock it.
X */
X if (IMMEDIATE & (1L << STRIP(ioreq->io_Command)))
X goto Immediate;
X
X /*
X * We may need to turn the active (lock) bit off, but not if we are
X * within the task.
X */
X if (unit->mu_Flags & UNITF_INTASK)
X goto Immediate;
X
X unit->mu_Flags &= ~UNITF_ACTIVE;
X
X /*
X * The task may have work to do that came in while we were processing
X * in the caller's context.
X */
X if (unit->mu_Flags & UNITF_WAKETASK) {
X unit->mu_Flags &= ~UNITF_WAKETASK;
X WakePort(&unit->mu_Port);
X }
X#endif
X
XImmediate:
X /*
X * If the quick bit is still set then wen don't need to reply the msg
X * -- just return to the user.
X */
X
X if (!(ioreq->io_Flags & IOF_QUICK))
X ReplyMsg(&ioreq->io_Message);
X
X return;
X}
X
X/*
X * AbortIO entry point. We don't abort IO here.
X */
X
Xlong
XDevAbortIO(D2, D3, ioreq, dev)
Xulong D2,
X D3;
XDEV *dev;
Xstruct IOStdReq *ioreq;
X{
X return 1;
X}
X
Xvoid
XWakePort(port)
Xregister struct MsgPort *port;
X{
X Signal(port->mp_SigTask, 1L << port->mp_SigBit);
X}
X
X/*
X * This is the main loop of the Unit tasks. It must be very careful with
X * global data.
X */
X
Xvoid
XUnitTask()
X{
X /* DEV *dev; */
X UNIT *unit;
X long waitmask;
X struct IOExtTD *ioreq;
X
X {
X struct Task *task,
X *FindTask();
X
X task = FindTask(NULL);
X unit = (UNIT *) task->tc_UserData;
X /* dev = unit->mu_Dev; */
X task->tc_UserData = NULL;
X }
X
X /*
X * Now finish initializing the message ports and other signal things
X */
X
X {
X byte sigbit;
X
X unit->mu_DiskReplyPort.mp_SigBit = AllocSignal(-1L);
X unit->mu_DiskReplyPort.mp_Flags = PA_SIGNAL;
X
X sigbit = AllocSignal(-1L);
X unit->mu_Port.mp_SigBit = sigbit;
X unit->mu_Port.mp_Flags = PA_SIGNAL;
X waitmask = 1L << sigbit;
X
X unit->mu_DmaSignal = AllocSignal(-1L);
X }
X
X for (;;) {
X debug(("Task: Waiting... "));
X Wait(waitmask);
X
X /*
X * See if we are stopped.
X */
X if (unit->mu_Flags & UNITF_STOPPED)
X continue;
X
X#ifdef HANDLE_IO_QUICK
X /*
X * Lock the device. If it fails, we have set a flag such that the
X * TermIO wakes us again.
X */
X unit->mu_Flags |= UNITF_WAKETASK;
X if (BSET_ACTIVE(&unit->mu_Flags))
X continue;
X
X unit->mu_Flags |= UNITF_INTASK;
X#endif
X
X while (ioreq = (struct IOExtTD *) GetMsg(&unit->mu_Port)) {
X debug(("Task: io %08lx %x\n", ioreq, ioreq->iotd_Req.io_Command));
X ioreq->iotd_Req.io_Error = 0;
X PerformIO((&ioreq->iotd_Req), unit);
X }
X
X#ifdef HANDLE_IO_QUICK
X unit->mu_Flags &= ~(UNITF_ACTIVE | UNITF_INTASK | UNITF_WAKETASK);
X#endif
X }
X}
X
Xvoid
XCMD_Invalid(ioreq, unit)
Xstruct IOStdReq *ioreq;
XUNIT *unit;
X{
X ioreq->io_Error = IOERR_NOCMD;
X TermIO(ioreq);
X}
X
Xvoid
XCMD_Stop(ioreq, unit)
Xstruct IOExtTD *ioreq;
XUNIT *unit;
X{
X unit->mu_Flags |= UNITF_STOPPED;
X TermIO(ioreq);
X}
X
Xvoid
XCMD_Start(ioreq, unit)
Xstruct IOExtTD *ioreq;
XUNIT *unit;
X{
X unit->mu_Flags &= ~UNITF_STOPPED;
X WakePort(&unit->mu_Port);
X TermIO(ioreq);
X}
X
Xvoid
XCMD_Flush(ioreq, unit)
Xstruct IOExtTD *ioreq;
XUNIT *unit;
X{
X register struct IOStdReq *req;
X
X /* Flush our own command queue */
X Forbid();
X while (req = (struct IOStdReq *) GetMsg(unit->mu_Port)) {
X req->io_Error = IOERR_ABORTED;
X ReplyMsg(&req->io_Message);
X }
X Permit();
X
X WakePort(&unit->mu_Port);
X TermIO(ioreq);
X}
X
Xvoid
XTrackdiskGateway(ioreq, unit)
Xregister struct IOExtTD *ioreq;
XUNIT *unit;
X{
X register struct IOExtTD *tdioreq;
X
X debug(("Trackdisk: %x ", ioreq->iotd_Req.io_Command));
X tdioreq = unit->mu_DiskIOReq;
X
X /*
X * Clone almost the entire io request to relay to the
X * trackdisk.device.
X */
X
X tdioreq->iotd_Req.io_Command = ioreq->iotd_Req.io_Command;
X tdioreq->iotd_Req.io_Flags = ioreq->iotd_Req.io_Flags | IOF_QUICK;
X tdioreq->iotd_Req.io_Length = ioreq->iotd_Req.io_Length;
X tdioreq->iotd_Req.io_Data = ioreq->iotd_Req.io_Data;
X tdioreq->iotd_Req.io_Offset = ioreq->iotd_Req.io_Offset;
X if (ioreq->iotd_Req.io_Command & TDF_EXTCOM) {
X tdioreq->iotd_Count = ioreq->iotd_Count;
X tdioreq->iotd_SecLabel = ioreq->iotd_SecLabel;
X }
X BeginIO(tdioreq);
X WaitIO(tdioreq);
X
X ioreq->iotd_Req.io_Error = tdioreq->iotd_Req.io_Error;
X ioreq->iotd_Req.io_Actual = tdioreq->iotd_Req.io_Actual;
X
X TermIO(ioreq);
X}
X
X#ifdef DEBUG
X/* DEBUGGING */
Xstruct MsgPort *Dbport; /* owned by the debug process */
Xstruct MsgPort *Dback; /* owned by the DOS device driver */
Xshort DBEnable;
Xstruct SignalSemaphore PortUse;
X
X#define CTOB(x) (void *)(((long)(x))>>2) /* BCPL conversion */
X
X/*
X * DEBUGGING CODE. You cannot make DOS library calls that access
X * other devices from within a device driver because the caller may not be
X * a process. If you need to make such calls you must create a port and
X * construct the DOS messages yourself. I do not do this. To get
X * debugging info out another PROCESS is created to which debugging
X * messages can be sent. The replyport gets a new SigTask for every
X * dbprintf call, therefore the semaphore.
X */
X
Xextern void debugproc();
Xstruct Library *DOSBase,
X *OpenLibrary();
X
Xdbinit()
X{
X struct Task *task = FindTask(NULL);
X
X DOSBase = OpenLibrary("dos.library", 0L);
X Dback = CreatePort("Dback", -1L);
X FreeSignal((long) Dback->mp_SigBit);
X Dback->mp_SigBit = 2;
X InitSemaphore(&PortUse);
X CreateProc("messydisk_DB", (long) TASKPRI + 1, CTOB(debugproc), 2000L);
X WaitPort(Dback); /* handshake startup */
X GetMsg(Dback); /* remove dummy msg */
X DBEnable = 1;
X dbprintf("Debugger running V1.11\n");
X}
X
Xdbuninit()
X{
X struct Message killmsg;
X
X if (Dbport) {
X killmsg.mn_Length = 0; /* 0 means die */
X ObtainSemaphore(&PortUse);
X Dback->mp_SigTask = FindTask(NULL);
X PutMsg(Dbport, &killmsg);
X WaitPort(Dback); /* He's dead jim! */
X GetMsg(Dback);
X ReleaseSemaphore(&PortUse);
X Dback->mp_SigBit = -1;
X DeletePort(Dback);
X
X /*
X * Since the debug process is running at a greater priority, I am
X * pretty sure that it is guarenteed to be completely removed
X * before this task gets control again.
X */
X }
X CloseLibrary(DOSBase);
X}
X
Xdbprintf(a, b, c, d, e, f, g, h, i, j)
Xlong a, b, c, d, e, f, g, h, i, j;
X{
X struct {
X struct Message msg;
X char buf[256];
X } msgbuf;
X register struct Message *msg = &msgbuf.msg;
X register long len;
X
X if (Dbport && DBEnable) {
X ObtainSemaphore(&PortUse); /* sprintf is not re-entrant */
X sprintf(msgbuf.buf, a, b, c, d, e, f, g, h, i, j);
X len = strlen(msgbuf.buf) + 1;
X msg->mn_Length = len; /* Length NEVER 0 */
X Dback->mp_SigTask = FindTask(NULL);
X PutMsg(Dbport, msg);
X WaitPort(Dback);
X GetMsg(Dback);
X ReleaseSemaphore(&PortUse);
X }
X}
X
X/*
X * BTW, the DOS library used by debugmain() was actually opened by the
X * opener of the device driver.
X */
X
Xdebugmain()
X{
X register struct Message *msg;
X register long len;
X register void *fh,
X *Open();
X void *fh2;
X struct Message DummyMsg;
X
X Dbport = CreatePort("Dbport", -1L);
X fh = Open("CON:0/20/640/101/Device debug", MODE_NEWFILE);
X fh2 = Open("PAR:", MODE_OLDFILE);
X PutMsg(Dback, &DummyMsg);
X for (;;) {
X WaitPort(Dbport);
X msg = GetMsg(Dbport);
X len = msg->mn_Length;
X if (len == 0)
X break;
X --len; /* Fix length up */
X if (DBEnable & 1)
X Write(fh, msg + 1, len);
X if (DBEnable & 2)
X Write(fh2, msg + 1, len);
X PutMsg(Dback, msg);
X }
X Close(fh);
X Close(fh2);
X DeletePort(Dbport);
X PutMsg(Dback, msg); /* Kill handshake */
X}
X
X/*
X * The assembly tag for the DOS process: CNOP causes alignment problems
X * with the Aztec assembler for some reason. I assume then, that the
X * alignment is unknown. Since the BCPL conversion basically zero's the
X * lower two bits of the address the actual code may start anywhere within
X * 8 bytes of address (remember the first longword is a segment pointer
X * and skipped). Sigh.... (see CreateProc() above).
X */
X/* INDENT OFF */
X#asm
X public _debugproc
X public _debugmain
X
X cseg
X_debugproc:
X nop
X nop
X nop
X nop
X nop
X movem.l D2-D7/A2-A6,-(sp)
X jsr _debugmain
X movem.l (sp)+,D2-D7/A2-A6
X rts
X#endasm
X
X#endif /* DEBUG */
END_OF_FILE
if test 18725 -ne `wc -c <'src/device.c'`; then
echo shar: \"'src/device.c'\" unpacked with wrong size!
fi
# end of 'src/device.c'
fi
if test -f 'src/hansec.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'src/hansec.c'\"
else
echo shar: Extracting \"'src/hansec.c'\" \(19261 characters\)
sed "s/^X//" >'src/hansec.c' <<'END_OF_FILE'
X/*-
X * $Id: hansec.c,v 1.4 90/02/10 21:30:54 Rhialto Exp $
X * $Log: hansec.c,v $
X * Revision 1.4 90/02/10 21:30:54 Rhialto
X * Tuned cache a bit.
X *
X * Revision 1.3 90/01/27 20:20:16 Rhialto
X * Sorted sectors when flushing cache
X *
X * Revision 1.2 90/01/23 02:31:50 Rhialto
X * Add 16-bit FAT support.
X *
X * Revision 1.1 89/12/17 20:02:49 Rhialto
X * Initial revision
X *
X * HANSEC.C
X *
X * The code for the messydos file system handler.
X *
X * Sector-level stuff: read, write, cache, unit conversion.
X * Other interactions (via MyDoIO) with messydisk.device.
X *
X * This code is (C) Copyright 1989,1990 by Olaf Seibert. All rights reserved.
X * May not be used or copied without a licence.
X-*/
X
X#include "han.h"
X#include "dos.h"
X
X/*#undef DEBUG /**/
X#ifdef DEBUG
X# define debug(x) dbprintf x
X#else
X# define debug(x)
X#endif
X
Xstruct MsgPort *DiskReplyPort;
Xstruct IOExtTD *DiskIOReq;
Xstruct IOStdReq *DiskChangeReq;
X
Xstruct DiskParam Disk;
Xbyte *Fat;
Xshort FatDirty; /* Fat must be written to disk */
X
Xshort error; /* To put the error value; for Result2 */
Xlong IDDiskState; /* InfoData.id_DiskState */
Xlong IDDiskType; /* InfoData.id_DiskType */
Xstruct timerequest *TimeIOReq; /* For motor-off delay */
Xstruct MinList CacheList; /* Sector cache */
Xint CurrentCache; /* How many cached buffers do we have */
Xint MaxCache = 5; /* Maximum amount of cached buffers */
Xlong CacheBlockSize; /* Size of disk block + overhead */
Xulong BufMemType;
Xint DelayState;
X
Xbyte *Word8086;
X
Xword
XGet8086Word(offset)
Xregister int offset;
X{
X return Word8086[offset] | Word8086[offset + 1] << 8;
X}
X
Xword
XOtherEndianWord(oew)
Xword oew;
X{
X/* INDENT OFF */
X#asm
X move.w 8(a5),d0
X rol.w #8,d0
X#endasm
X /* INDENT ON */
X /*
X * return (oew << 8) | ((oew >> 8) & 0xff);
X */
X}
X
Xulong
XOtherEndianLong(oel)
Xulong oel;
X{
X/* INDENT OFF */
X#asm
X move.l 8(a5),d0
X rol.w #8,d0
X swap d0
X rol.w #8,d0
X#endasm
X /* INDENT ON */
X /*
X * return ((oel & 0xff) << 24) | ((oel & 0xff00) << 8) |
X * ((oel & 0xff0000) >> 8) | ((oel & 0xff000000) >> 24);
X */
X}
X
Xvoid
XOtherEndianMsd(msd)
Xregister struct MsDirEntry *msd;
X{
X msd->msd_Date = OtherEndianWord(msd->msd_Date);
X msd->msd_Time = OtherEndianWord(msd->msd_Time);
X msd->msd_Cluster = OtherEndianWord(msd->msd_Cluster);
X msd->msd_Filesize = OtherEndianLong(msd->msd_Filesize);
X}
X
Xword
XClusterToSector(cluster)
Xregister word cluster;
X{
X return cluster ? Disk.start + cluster * Disk.spc
X : 0;
X}
X
Xword
XClusterOffsetToSector(cluster, offset)
Xregister word cluster;
Xregister word offset;
X{
X return cluster ? Disk.start + cluster * Disk.spc + offset / Disk.bps
X : 0;
X}
X
Xword
XDirClusterToSector(cluster)
Xregister word cluster;
X{
X return cluster ? Disk.start + cluster * Disk.spc
X : Disk.rootdir;
X}
X
Xword
XSectorToCluster(sector)
Xregister word sector;
X{
X return sector ? (sector - Disk.start) / Disk.spc
X : 0;
X}
X
X/*
X * Get the next cluster in a chain. Sort-of checks for special entries.
X */
X
Xword
XNextCluster(cluster)
Xword cluster;
X{
X register word entry;
X
X return (entry = GetFatEntry(cluster)) >= 0xFFF0 ? FAT_EOF : entry;
X}
X
Xword
XNextClusteredSector(sector)
Xword sector;
X{
X word next = (sector + 1 - Disk.start) % Disk.spc;
X
X if (next == 0) {
X next = NextCluster(SectorToCluster(sector));
X return next != FAT_EOF ? ClusterToSector(next)
X : SEC_EOF;
X } else
X return sector + 1;
X}
X
X#ifndef READONLY
X
Xword
XFindFreeSector(prev)
Xword prev;
X{
X word freecluster = FindFreeCluster(SectorToCluster(prev));
X
X return freecluster == FAT_EOF ? SEC_EOF : ClusterToSector(freecluster);
X}
X
X#endif
X
X/*
X * Find a specific sector. The cache list is a Least Recently Used stack:
X * Put it on the head of the cache list. So if it is not used anymore in a
X * long time, it bubbles to the end of the list, getting a higher chance
X * of being trashed for re-use.
X */
X
Xstruct CacheSec *
XFindSecByNumber(number)
Xregister int number;
X{
X register struct CacheSec *sec;
X register struct CacheSec *nextsec;
X
X debug(("FindSecByNumber %d", number));
X
X for (sec = (void *) CacheList.mlh_Head;
X nextsec = (void *) sec->sec_Node.mln_Succ; sec = nextsec) {
X if (sec->sec_Number == number) {
X debug((" (%x) %lx\n", sec->sec_Refcount, sec));
X Remove(sec);
X AddHead(&CacheList, &sec->sec_Node);
X return sec;
X }
X }
X
X debug(("; "));
X return NULL;
X}
X
Xstruct CacheSec *
XFindSecByBuffer(buffer)
Xbyte *buffer;
X{
X return (struct CacheSec *) (buffer - OFFSETOF(CacheSec, sec_Data));
X}
X
X/*
X * Get a fresh cache buffer. If we are allowed more cache, we just
X * allocate memory. Otherwise, we try to find a currently unused buffer.
X * We start looking at the end of the list, which is the bottom of the LRU
X * stack. If that fails, allocate more memory anyway. Not that is likely
X * anyway, since we currently lock only one sector at a time.
X */
X
Xstruct CacheSec *
XNewCacheSector()
X{
X register struct CacheSec *sec;
X register struct CacheSec *nextsec;
X
X debug(("NewCacheSector\n"));
X
X if (CurrentCache < MaxCache) {
X if (sec = AllocMem(CacheBlockSize, BufMemType)) {
X goto add;
X }
X }
X for (sec = (void *) CacheList.mlh_TailPred;
X nextsec = (void *) sec->sec_Node.mln_Pred; sec = nextsec) {
X if ((CurrentCache >= MaxCache) && (sec->sec_Refcount == SEC_DIRTY)) {
X FreeCacheSector(sec); /* Also writes it to disk */
X continue;
X }
X if (sec->sec_Refcount == 0) /* Implies not SEC_DIRTY */
X return sec;
X }
X
X sec = AllocMem(CacheBlockSize, BufMemType);
X
X if (sec) {
Xadd:
X CurrentCache++;
X AddHead(&CacheList, &sec->sec_Node);
X } else
X error = ERROR_NO_FREE_STORE;
X
X return sec;
X}
X
X/*
X * Dispose a cached sector, even if it has a non-zero refcount. If it is
X * dirty, write it out.
X */
X
Xvoid
XFreeCacheSector(sec)
Xregister struct CacheSec *sec;
X{
X debug(("FreeCacheSector %d\n", sec->sec_Number));
X Remove(sec);
X#ifndef READONLY
X if (sec->sec_Refcount & SEC_DIRTY) {
X PutSec(sec->sec_Number, sec->sec_Data);
X }
X#endif
X FreeMem(sec, CacheBlockSize);
X CurrentCache--;
X}
X
X/*
X * Create an empty cache list
X */
X
Xvoid
XInitCacheList()
X{
X extern struct CacheSec *sec; /* Of course this does not exist... */
X
X NewList(&CacheList);
X CurrentCache = 0;
X CacheBlockSize = Disk.bps + sizeof (*sec) - sizeof (sec->sec_Data);
X}
X
X/*
X * Dispose all cached sectors, possibly writing them to disk.
X */
X
Xvoid
XFreeCacheList()
X{
X register struct CacheSec *sec;
X
X debug(("FreeCacheList, %d\n", CurrentCache));
X while (sec = GetHead(&CacheList)) {
X FreeCacheSector(sec);
X }
X}
X
X/*
X * Do an insertion sort on tosort in the CacheList. Since it changes the
X * location in the list, you must fetch it before calling this routine.
X * The list will become ascending.
X */
X
Xvoid
XSortSec(tosort)
Xregister struct CacheSec *tosort;
X{
X register struct CacheSec *sec;
X struct CacheSec *nextsec;
X register word secno;
X
X secno = tosort->sec_Number;
X debug(("SortSec %d: ", secno));
X
X for (sec = (void *) CacheList.mlh_Head;
X nextsec = (void *) sec->sec_Node.mln_Succ; sec = nextsec) {
X debug(("%d, ", sec->sec_Number));
X if (sec == tosort) {
X debug(("\n"));
X return; /* No need to move it away */
X }
X if (sec->sec_Number > secno)
X break;
X }
X /* Insert before sec */
X Remove(tosort);
X Insert(&CacheList, tosort, sec->sec_Node.mln_Pred);
X debug(("\n"));
X}
X
X/*
X * Write all dirty cache buffers to disk. They are written from highest to
X * lowest, and then the FAT is written out.
X */
X
Xvoid
XMSUpdate(immediate)
Xint immediate;
X{
X register struct CacheSec *sec;
X register struct CacheSec *nextsec;
X
X debug(("MSUpdate\n"));
X
X#ifndef READONLY
X if (DelayState & DELAY_DIRTY) {
X /*
X * First sort all dirty sectors on block number
X */
X for (sec = (void *) CacheList.mlh_Head;
X nextsec = (void *) sec->sec_Node.mln_Succ; sec = nextsec) {
X if (sec->sec_Refcount & SEC_DIRTY) {
X SortSec(sec);
X }
X }
X /*
X * Then do a second (backward) scan to write them out.
X */
X for (sec = (void *) CacheList.mlh_TailPred;
X nextsec = (void *) sec->sec_Node.mln_Pred; sec = nextsec) {
X if (sec->sec_Refcount & SEC_DIRTY) {
X PutSec(sec->sec_Number, sec->sec_Data);
X sec->sec_Refcount &= ~SEC_DIRTY;
X }
X }
X DelayState &= ~DELAY_DIRTY;
X }
X if (FatDirty) {
X WriteFat();
X }
X#endif
X
X if (immediate)
X DelayState = DELAY_RUNNING1;
X
X if (DelayState & DELAY_RUNNING2) {
X StartTimer();
X DelayState &= ~DELAY_RUNNING2;
X } else { /* DELAY_RUNNING1 */
X#ifndef READONLY
X while (TDUpdate() != 0 && RetryRwError(DiskIOReq))
X ;
X#endif
X TDMotorOff();
X DelayState = DELAY_OFF;
X }
X}
X
X/*
X * Start the timer which triggers cache writing and stopping the disk
X * motor.
X */
X
Xvoid
XStartTimer()
X{
X DelayState |= DELAY_RUNNING1 | DELAY_RUNNING2;
X
X if (CheckIO(TimeIOReq)) {
X WaitIO(TimeIOReq);
X TimeIOReq->tr_node.io_Command = TR_ADDREQUEST;
X TimeIOReq->tr_time.tv_secs = 3;
X TimeIOReq->tr_time.tv_micro = 0;
X SendIO(TimeIOReq);
X }
X}
X
X/*
X * Get a pointer to a logical sector { 0, ..., MS_SECTCNT - 1}. We
X * allocate a buffer and copy the data in, and lock the buffer until
X * FreeSec() is called.
X */
X
Xbyte *
XGetSec(sector)
Xint sector;
X{
X struct CacheSec *sec;
X
X if (sec = FindSecByNumber(sector)) {
X sec->sec_Refcount++;
X
X return sec->sec_Data;
X }
X if (sec = NewCacheSector()) {
X register struct IOExtTD *req;
X
X sec->sec_Number = sector;
X sec->sec_Refcount = 1;
X
X debug(("GetSec %d\n", sector));
X
X req = DiskIOReq;
X do {
X req->iotd_Req.io_Command = ETD_READ;
X req->iotd_Req.io_Data = (APTR)sec->sec_Data;
X req->iotd_Req.io_Offset = Disk.lowcyl + (long) sector * Disk.bps;
X req->iotd_Req.io_Length = Disk.bps;
X MyDoIO(req);
X } while (req->iotd_Req.io_Error != 0 && RetryRwError(req));
X
X StartTimer();
X
X if (req->iotd_Req.io_Error == 0) {
X return sec->sec_Data;
X }
X error = ERROR_NOT_A_DOS_DISK;
X FreeCacheSector(sec);
X }
X return NULL;
X}
X
X#ifndef READONLY
X
Xbyte *
XEmptySec(sector)
Xint sector;
X{
X byte *buffer;
X register struct CacheSec *sec;
X
X if (sec = FindSecByNumber(sector)) {
X sec->sec_Refcount++;
X
X return sec->sec_Data;
X }
X if (sec = NewCacheSector()) {
X sec->sec_Number = sector;
X sec->sec_Refcount = 1;
X
X return sec->sec_Data;
X }
X
X return NULL;
X}
X
Xvoid
XPutSec(sector, data)
Xint sector;
Xbyte *data;
X{
X register struct IOExtTD *req;
X
X debug(("PutSec %d\n", sector));
X
X req = DiskIOReq;
X do {
X req->iotd_Req.io_Command = ETD_WRITE;
X req->iotd_Req.io_Data = (APTR) data;
X req->iotd_Req.io_Offset = Disk.lowcyl + (long) sector * Disk.bps;
X req->iotd_Req.io_Length = Disk.bps;
X MyDoIO(req);
X } while (req->iotd_Req.io_Error != 0 && RetryRwError(req));
X
X StartTimer();
X}
X
X#endif
X
X/*
X * Unlock a cached sector. When the usage count drops to zero, which
X * implies it is not dirty, and we are over our cache quota, the sector is
X * freed. Otherwise we keep it for re-use.
X */
X
Xvoid
XFreeSec(buffer)
Xbyte *buffer;
X{
X register struct CacheSec *sec;
X
X if (sec = FindSecByBuffer(buffer)) {
X if (--sec->sec_Refcount == 0) { /* Implies not SEC_DIRTY */
X if (CurrentCache > MaxCache) {
X FreeCacheSector(sec);
X }
X }
X }
X}
X
X#ifndef READONLY
X
Xvoid
XMarkSecDirty(buffer)
Xbyte *buffer;
X{
X register struct CacheSec *sec;
X
X if (sec = FindSecByBuffer(buffer)) {
X sec->sec_Refcount |= SEC_DIRTY;
X DelayState |= DELAY_DIRTY;
X StartTimer();
X }
X}
X
X/*
X * Write out the FAT. Called from MSUpdate(), so don't call it again from
X * here. Don't use precious cache space for it; you could say it has its
X * own private cache already.
X */
X
Xvoid
XWriteFat()
X{
X register int fat,
X sec;
X int disksec = Disk.res; /* First FAT, first sector */
X
X /* Write all FATs */
X for (fat = 0; fat < Disk.nfats; fat++) {
X for (sec = 0; sec < Disk.spf; sec++) {
X PutSec(disksec++, Fat + sec * Disk.bps);
X /* return; /* Fat STILL dirty! */
X }
X }
X FatDirty = FALSE;
X}
X
X#endif
X
Xint
XReadBootBlock()
X{
X int protstatus;
X
X debug(("ReadBootBlock\n"));
X FreeFat(); /* before disk parameters change */
X TDClear();
X
X if ((protstatus = TDProtStatus()) >= 0) {
X TDChangeNum();
X debug(("Changenumber = %ld\n", DiskIOReq->iotd_Count));
X if (Word8086 = GetSec(0)) {
X word bps;
X
X /* 8086 ml for a jump */
X if (Word8086[0] != 0xE9 && Word8086[0] != 0xEB) {
X goto nodisk;
X }
X bps = Get8086Word(0x0b);
X Disk.spc = Word8086[0x0d];
X Disk.res = Get8086Word(0x0e);
X Disk.nfats = Word8086[0x10];
X Disk.ndirs = Get8086Word(0x11);
X Disk.nsects = Get8086Word(0x13);
X Disk.media = Word8086[0x15];
X Disk.spf = Get8086Word(0x16);
X Disk.spt = Get8086Word(0x18);
X Disk.nsides = Get8086Word(0x1a);
X Disk.nhid = Get8086Word(0x1c);
X FreeSec(Word8086);
X
X /*
X * Maybe the sector size just changed. Who knows?
X */
X if (Disk.bps != bps) {
X FreeCacheList();
X Disk.bps = bps;
X InitCacheList();
X }
X
X Disk.ndirsects = (Disk.ndirs * MS_DIRENTSIZE) / Disk.bps;
X Disk.rootdir = Disk.res + Disk.spf * Disk.nfats;
X Disk.datablock = Disk.rootdir + Disk.ndirsects;
X Disk.start = Disk.datablock - MS_FIRSTCLUST * Disk.spc;
X /* Available clusters are 2..maxclust in secs start..nsects-1 */
X Disk.maxclst = (Disk.nsects - Disk.start) / Disk.spc - 1;
X Disk.bpc = Disk.bps * Disk.spc;
X Disk.vollabel = FakeRootDirEntry;
X/* Disk.fat16bits = Disk.nsects > 20740; /* DOS3.2 magic value */
X Disk.fat16bits = Disk.maxclst > 0xFF6; /* DOS3.0 magic value */
X
X debug(("%x\tbytes per sector\n", Disk.bps));
X debug(("%x\tsectors per cluster\n", Disk.spc));
X debug(("%x\treserved blocks\n", Disk.res));
X debug(("%x\tfats\n", Disk.nfats));
X debug(("%x\tdirectory entries\n", Disk.ndirs));
X debug(("%x\tsectors\n", Disk.nsects));
X debug(("%x\tmedia byte\n", Disk.media));
X debug(("%x\tsectors per FAT\n", Disk.spf));
X debug(("%x\tsectors per track\n", Disk.spt));
X debug(("%x\tsides\n", Disk.nsides));
X debug(("%x\thidden sectors\n", Disk.nhid));
X
X debug(("%x\tdirectory sectors\n", Disk.ndirsects));
X debug(("%x\troot dir block\n", Disk.rootdir));
X debug(("%x\tblock for (imaginary) cluster 0\n", Disk.start));
X debug(("%x\tfirst data block\n", Disk.datablock));
X debug(("%x\tclusters total\n", Disk.maxclst));
X debug(("%x\tbytes per cluster\n", Disk.bpc));
X debug(("%x\t16-bits FAT?\n", Disk.fat16bits));
X
X IDDiskType = ID_DOS_DISK;
X#ifdef READONLY
X IDDiskState = ID_WRITE_PROTECTED;
X#else
X if (protstatus > 0)
X IDDiskState = ID_WRITE_PROTECTED;
X else
X IDDiskState = ID_VALIDATED;
X#endif
X
X if (Disk.nsects / (MS_SPT * Disk.nsides) <= 40)
X DiskIOReq->iotd_Req.io_Flags |= IOMDF_40TRACKS;
X else
X DiskIOReq->iotd_Req.io_Flags &= ~IOMDF_40TRACKS;
X
X GetFat();
X } else {
X debug(("Can't read %d.\n", DiskIOReq->iotd_Req.io_Error));
X nodisk:
X FreeCacheList();
X error = ERROR_NO_DISK;
X IDDiskType = ID_UNREADABLE_DISK;
X IDDiskState = ID_WRITE_PROTECTED;
X }
X }
X#ifdef DEBUG
X else debug(("No disk inserted %d.\n", DiskIOReq->iotd_Req.io_Error));
X#endif
X return 1;
X}
X
X/*
X * We try to identify the disk currently in the drive, trying to find the
X * volume label in the first directory block.
X */
X
Xint
XIdentifyDisk(name, date)
Xchar *name; /* Should be at least 32 characters */
Xstruct DateStamp *date;
X{
X debug(("IdentifyDisk\n"));
X ReadBootBlock(); /* Also sets default vollabel */
X
X if (IDDiskType == ID_DOS_DISK) {
X byte *dirblock;
X register struct MsDirEntry *dirent;
X
X if (dirblock = GetSec(Disk.rootdir)) {
X dirent = (struct MsDirEntry *) dirblock;
X
X while ((byte *) dirent < &dirblock[Disk.bps]) {
X if (dirent->msd_Attributes & ATTR_VOLUMELABEL) {
X Disk.vollabel.de_Msd = *dirent;
X Disk.vollabel.de_Sector = Disk.rootdir;
X Disk.vollabel.de_Offset = (byte *) dirent - dirblock;
X OtherEndianMsd(&Disk.vollabel.de_Msd);
X Disk.vollabel.de_Msd.msd_Cluster = 0; /* to be sure */
X break;
X }
X dirent++;
X }
X strncpy(name, Disk.vollabel.de_Msd.msd_Name, 8 + 3);
X name[8 + 3] = '\0';
X ZapSpaces(name, name + 8 + 3);
X ToDateStamp(date, Disk.vollabel.de_Msd.msd_Date,
X Disk.vollabel.de_Msd.msd_Time);
X debug(("Disk is called '%s'\n", name));
X
X FreeSec(dirblock);
X
X return 0;
X }
X }
X return 1;
X}
X
X/*
X * Remove the disk change SoftInt. The V1.2 / V1.3 version is broken, so
X * we use a workaround. The correct thing to do is shown but not used.
X */
X
Xvoid
XTDRemChangeInt()
X{
X if (DiskChangeReq) {
X register struct IOExtTD *req = DiskIOReq;
X
X#if 0 /* V1.2 and V1.3 have a broken
X * TD_REMCHANGEINT */
X req->iotd_Req.io_Command = TD_REMCHANGEINT;
X req->iotd_Req.io_Data = (void *) DiskChangeReq;
X MyDoIO(req);
X WaitIO(DiskChangeReq);
X#else
X Forbid();
X Remove(DiskChangeReq);
X Permit();
X#endif
X DeleteExtIO(DiskChangeReq);
X DiskChangeReq = NULL;
X }
X}
X
X/*
X * Set the disk change SoftInt. Return nonzero on failure.
X */
X
Xint
XTDAddChangeInt(interrupt)
Xstruct Interrupt *interrupt;
X{
X register struct IOExtTD *req = DiskIOReq;
X
X if (DiskChangeReq) {
X TDRemChangeInt();
X }
X DiskChangeReq = (void *)CreateExtIO(DiskReplyPort,
X (long) sizeof (*DiskChangeReq));
X if (DiskChangeReq) {
X /* Clone IO request part */
X DiskChangeReq->io_Device = req->iotd_Req.io_Device;
X DiskChangeReq->io_Unit = req->iotd_Req.io_Unit;
X DiskChangeReq->io_Command = TD_ADDCHANGEINT;
X DiskChangeReq->io_Data = (void *) interrupt;
X SendIO(DiskChangeReq);
X
X return 0;
X }
X return 1;
X}
X
X/*
X * Get the current disk change number. Necessary for ETD_ commands. Makes
X * absolutely sure nobody can change the disk without us noticing it.
X */
X
Xint
XTDChangeNum()
X{
X register struct IOExtTD *req = DiskIOReq;
X
X req->iotd_Req.io_Command = TD_CHANGENUM;
X MyDoIO(req);
X req->iotd_Count = req->iotd_Req.io_Actual;
X
X return req->iotd_Req.io_Actual;
X}
X
X/*
X * Get the current write protection state.
X *
X * Zero means writable, one means write protected, minus one means
X * no disk in drive.
X */
X
Xint
XTDProtStatus()
X{
X register struct IOExtTD *req = DiskIOReq;
X
X req->iotd_Req.io_Command = TD_PROTSTATUS;
X MyDoIO(req);
X
X if (req->iotd_Req.io_Error)
X return -1;
X
X return req->iotd_Req.io_Actual != 0;
X}
X
X/*
X * Switch the drive motor off. Return previous state. Don't use this when
X * you have allocated the disk via GetDrive().
X */
X
Xint
XTDMotorOff()
X{
X register struct IOExtTD *req = DiskIOReq;
X
X req->iotd_Req.io_Command = TD_MOTOR;
X req->iotd_Req.io_Length = 0;
X MyDoIO(req);
X
X return req->iotd_Req.io_Actual;
X}
X
X/*
X * Clear all internal messydisk buffers.
X */
X
Xint
XTDClear()
X{
X register struct IOExtTD *req = DiskIOReq;
X
X req->iotd_Req.io_Command = CMD_CLEAR;
X
X return MyDoIO(req);
X}
X
X#ifndef READONLY
X/*
X * Write out all internal messydisk buffers to the disk.
X */
X
Xint
XTDUpdate()
X{
X register struct IOExtTD *req = DiskIOReq;
X
X req->iotd_Req.io_Command = ETD_UPDATE;
X
X return MyDoIO(req);
X}
X#endif
X
Xint
XMyDoIO(ioreq)
Xregister struct IOStdReq *ioreq;
X{
X ioreq->io_Flags |= IOF_QUICK; /* Preserve IOMDF_40TRACKS */
X BeginIO(ioreq);
X return WaitIO(ioreq);
X}
END_OF_FILE
if test 19261 -ne `wc -c <'src/hansec.c'`; then
echo shar: \"'src/hansec.c'\" unpacked with wrong size!
fi
# end of 'src/hansec.c'
fi
echo shar: End of archive 3 \(of 6\).
cp /dev/null ark3isdone
MISSING=""
for I in 1 2 3 4 5 6 ; do
if test ! -f ark${I}isdone ; then
MISSING="${MISSING} ${I}"
fi
done
if test "${MISSING}" = "" ; then
echo You have unpacked all 6 archives.
rm -f ark[1-9]isdone
else
echo You still need to unpack the following archives:
echo " " ${MISSING}
fi
## End of shell archive.
exit 0
--
Mail submissions (sources or binaries) to <amiga@cs.odu.edu>.
Mail comments to the moderator at <amiga-request@cs.odu.edu>.
Post requests for sources, and general discussion to comp.sys.amiga.